local string = string

local function parseValue2(str)
    return string.match(str, "([%d%.-]+) ([%d%.-]+)")
end

local function parseValue3(str)
    return string.match(str, "([%d%.-]+) ([%d%.-]+) ([%d%.-]+)")
end

local function parseValue4(str)
    return string.match(str, "([%d%.-]+) ([%d%.-]+) ([%d%.-]+) ([%d%.-]+)")
end

local function getSize(fname)
    return function(node)
        local s = node[fname](node)
        return s.width .. ' ' .. s.height
    end
end
local function setSize(fname)
    return function(node, str)
        node[fname](node, CCSize(parseValue2(str)))
    end
end 

local function getPos(fname)
    return function(node)
        local p= node[fname](node)
        return string.format("%g %g", p.x, p.y)
    end
end

local function setPos(fname)
    return function(node, str)
        node[fname](node, ccp(parseValue2(str)))
    end
end

local function getBool(fname)
    return function(node)
        return node[fname](node) and "true" or "false"
    end
end

local function setBool(fname)
    return function(node, str)
        node[fname](node, string.lower(str) == 'true')
    end
end

local function getNumber(fname)
    return function(node) 
        return tostring(node[fname](node)) 
    end
end

local function setNumber(fname)
    return function(node, str) 
        node[fname](node, tonumber(str))
    end
end

local function getStr(fname)
    return function(node)
        return node[fname](node)
    end
end

local function setStr(fname)
    return function(node, str)
        node[fname](node, str)
    end
end

local function setRect(fname)
    return function(node, str)
        node[fname](node, CCRect(parseValue4(str)))
    end
end

local function getRect(fname)
    return function(node)
        local rect = node[fname](node)
        return string.format("%g %g %g %g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)
    end
end

--just ccc3
local function getColor(fname)
    return function(node)
        local c = node[fname](node)
        return string.format("%d %d %d", c.r, c.g, c.b)
    end
end

local function setColor(fname)
    return function(node, str)
        node[fname](node, ccc3(parseValue3(str)))
    end
end
--just ccc4
local function getColor4(fname)
    return function(node)
        local c = node[fname](node)
        return string.format("%d %d %d %d", c.r, c.g, c.b, c.a)
    end
end

local function setColor4(fname)
    return function(node, str)
        node[fname](node, ccc4(parseValue4(str)))
    end
end

--
local function fmt(str) return str end
local function fmt_str(str) return string.format('%q', str) end
local function fmt_bool(str) return string.lower(str) end
local function fmt_pos(str) return string.format('ccp(%g, %g)', parseValue2(str)) end
local function fmt_size(str) return string.format('CCSize(%g, %g)', parseValue2(str)) end
local function fmt_rect(str) return string.format('CCRect(%g, %g, %g, %g)', parseValue4(str)) end
local function fmt_color(str) return string.format('ccc3(%g, %g, %g)', parseValue3(str)) end
local function fmt_color4(str) return string.format('ccc4(%g, %g, %g, %g)', parseValue4(str)) end

local Accessor = {
    string      = {get = getStr,    set = setStr,    fmt=fmt_str},
    file        = {get = getStr,    set = setStr,    fmt=fmt_str},
    bool        = {get = getBool,   set = setBool,   fmt=fmt_bool},
    float       = {get = getNumber, set = setNumber, fmt=fmt},
    int         = {get = getNumber, set = setNumber, fmt=fmt},
    pos         = {get = getPos,    set = setPos,    fmt=fmt_pos},
    size        = {get = getSize,   set = setSize,   fmt=fmt_size},
    rect        = {get = getRect,   set = setRect,   fmt=fmt_rect},
    color       = {get = getColor,  set = setColor,  fmt=fmt_color},
    color4      = {get = getColor4, set = setColor4, fmt=fmt_color4},
}

local function setNothing()end

local function Prop(name, get, set, type, def)
    return {
        fmt = function(str) return string.format('%s(%s)', set, Accessor[type].fmt(str)) end,
        name = name,
        get  = Accessor[type].get(get),
        set  = set and Accessor[type].set(set) or setNothing,
        type = type,
        def  = def,
        readonly = (not set)
    }
end

local function EnumProp(name, get, set, type, def, choices)

    local getter = function(node)
        local value = node[get](node)
        for i, v in ipairs(choices) do
            if value == v[2] then
                return tostring(i - 1)
            end
        end
        error()
    end

    local setter = function(node, str)
        node[set](node, choices[tonumber(str)+1][2])
    end

    return {
        fmt  = function(str) return string.format('%s(%s)', set, choices[tonumber(str)+1][3] or choices[tonumber(str)+1][2]) end,-- 如果没配置名字。那么直接传入值
        name = name,
        get  = getter,
        set  = setter,
        type = type,
        def  = def,
        choices = choices,
    }
end

local defines = {}

local function Attribute(name, base_name, self)
    if base_name then
        local newself = {}

        local base = defines[base_name]
        local hash = {}

        for i, v in ipairs(self) do
            hash[v.name] = v
        end

        for i=#base, 1, -1 do
            local v = base[i]
            if hash[v.name] then
                table.insert(newself, 1, hash[v.name])
            else
                table.insert(newself, 1, v)
                hash[v.name] = nil
            end
        end

        for i, v in ipairs(self) do
            if hash[v.name] then
                table.insert(newself, v)
            end
        end

        newself.ctor = self.ctor
        newself._custom = self._custom
        newself.editor_setup = self.editor_setup
        
        self = newself
    end

    for i, v in ipairs(self) do
        self[v.name] = v
    end

    defines[name] = self

    return self
end


local function  Label_setup(inst)
    inst:setText("I'am label!!");
    inst:setFontSize(20);
end

local function ImageView_setup(inst)
    inst:loadTexture('image.png')
end

local function LabelBMFont_setup(inst)
    inst:setFntFile("bmfont.fnt")
    inst:setText('1234567890')-- =. =!!
end

local function Button_setup(inst)
    inst:loadTextureNormal("Button_Normal.png")
end

local function Layout_setup(inst)
    inst:setSize(CCSize(100, 100))
end

local function LoadingBar_setup(inst)
    inst:loadTexture('LoadingBarFile.png')
end

local function CheckBox_setup(inst)
    inst:loadTextureBackGround("CheckBox_Normal.png")
    inst:loadTextureFrontCross("CheckBoxNode_Normal.png")
end

local function Slider_setup(inst)
    inst:loadBarTexture('Slider_PressBar.png')
    inst:loadSlidBallTextureNormal('SliderNode_Normal.png')
end

local function TextField_setup(inst)
    inst:setPlaceHolder("Input text here.");
    inst:setFontSize(20);
end

local function ListView_setup(inst)
    inst:setSize(CCSize(100, 100))
end

local function ScrollView_setup(inst)
    inst:setSize(CCSize(100, 100))
end

local function PageView_setup(inst)
    inst:setSize(CCSize(100, 100))
end

local function LabelAtlas_setup(inst)
    inst:setTexture('TextAtlas.png')
    inst:setCharSize(CCSize(14, 18))
    inst:setStartChar(".")
    inst:setText('1234567890')
end

Attribute("Widget", nil, {
    Prop("class",       "getDescription",           nil,                "string",     ""),
    Prop("Name",        "getName",                  "setName",          "string",   ""),
    Prop("IgnoreSize",  "isIgnoreContentAdaptWithSize",                 "ignoreContentAdaptWithSize", "bool", "true"),
    Prop("Size",        "getSize",                  "setSize",          "size",     "0 0"),
    Prop("Pos",         "getPositionInCCPoint",     "setPosition",      "pos",      "0 0"),
    Prop("Anchor Pos",  "getAnchorPoint",           "setAnchorPoint",   "pos",      "0.5 0.5"),
    Prop("Rotation",    "getRotation",              "setRotation",      "float",    "0"),
    Prop("ZOrder",      "getZOrder",                "setZOrder",        "int",      "0"),
    Prop("Tag",         "getTag",                   "setTag",           "int",      "-1"),
    Prop("ScaleX",      "getScaleX",                "setScaleX",        "float",    "1"),
    Prop("ScaleY",      "getScaleY",                "setScaleY",        "float",    "1"),
    Prop("Visible",     "isVisible",                "setVisible",       "bool",     "true"),
    Prop("Opacity",     "getOpacity",               "setOpacity",       "int",      "255"),
    Prop('Color',       "getColor",                 "setColor",         "color",    "255 255 255"),
})

Attribute("ImageView", "Widget", {
    editor_setup = ImageView_setup,
    Prop("Texture",     "getTexture",               "loadTexture",      "file",     ""),
    Prop("Scale9",      "isScale9Enabled",          "setScale9Enabled", "bool",     "false"),
    Prop("CapInsets",   "getCapInsets",             "setCapInsets",     "rect",     "0 0 0 0"),
})

Attribute("Label", "Widget", {
    editor_setup = Label_setup,
    Prop("Text",        "getStringValue",           "setText",          "string",   ""),
    Prop("Font Size",   "getFontSize",              "setFontSize",      "int",      "10"),
    Prop("Font Name",   "getFontName",              "setFontName",      "string",   "Thonburi"),
    Prop("Border Enabled",   "getBorderEnabled",              "setBorderEnabled",      "bool",   "false"),
    Prop("Border Color",   "getBorderColor",              "setBorderColor",      "color4",   "255 255 255 255"),


    EnumProp('HAnign',  'getTextHorizontalAlignment',   'setTextHorizontalAlignment', 'enum', '0', {
        {"Left",   kCCTextAlignmentLeft,   'kCCTextAlignmentLeft'},
        {"Center", kCCTextAlignmentCenter, 'kCCTextAlignmentCenter'},
        {"Right",  kCCTextAlignmentRight,  'kCCTextAlignmentRight'},
    }),

    EnumProp('VAnign',  'getTextVerticalAlignment',   'setTextVerticalAlignment', 'enum', '0', {
        {"Top",    kCCVerticalTextAlignmentTop   , 'kCCVerticalTextAlignmentTop'},
        {"Center", kCCVerticalTextAlignmentCenter, 'kCCVerticalTextAlignmentCenter'},
        {"Bottom", kCCVerticalTextAlignmentBottom, 'kCCVerticalTextAlignmentBottom'},
    }),
})

Attribute("LabelBMFont", "Widget", {
    editor_setup = LabelBMFont_setup,
    Prop("Text",        "getStringValue",           "setText",          "string",   ""),
    Prop("Font File",   "getFntFile",               "setFntFile",       "file",   ""),
})

Attribute("LabelAtlas", "Widget", {
    editor_setup = LabelAtlas_setup,
    Prop("Text",        "getText",                  "setText",          "string",   ""),
    Prop("CharSize",    "getCharSize",               "setCharSize",     "size",     "0 0"),
    Prop("ShartChar",    "getStartChar",               "setStartChar",  "string",     "0"),
    Prop("Texture",     "getTexture",               "setTexture",       "file",     ""),
})

Attribute("Button", "Widget", {
    editor_setup = Button_setup,

    Prop("Normal",     "getTextureNormal",          "loadTextureNormal",      "file",     ""),
    Prop("Pressed",    "getTexturePressed",         "loadTexturePressed",     "file",     ""),
    Prop("Disabled",   "getTextureDisabled",        "loadTextureDisabled",    "file",     ""),

    Prop("Scale9",     "isScale9Enabled",          "setScale9Enabled",        "bool",     "false"),

    Prop("Title",     "getTitleText",          "setTitleText",        "string",     ""),
    Prop("TitleColor",     "getTitleColor",          "setTitleColor",        "color",     "255 255 255"),
    Prop("TitleFontSize",     "getTitleFontSize",          "setTitleFontSize",        "int",     "0"),
    Prop("TitleFontName",     "getTitleFontName",          "setTitleFontName",        "string",     "Helvetica"),

    Prop("Normal CapInsets",   "getCapInsetNormalRenderer",             "setCapInsetsNormalRenderer",     "rect",     "0 0 0 0"),
    Prop("Pressed CapInsets",   "getCapInsetPressedRenderer",             "setCapInsetsPressedRenderer",     "rect",     "0 0 0 0"),
    Prop("Disable CapInsets",   "getCapInsetDisabledRenderer",             "setCapInsetsDisabledRenderer",     "rect",     "0 0 0 0"),
})

Attribute("LoadingBar", "Widget", {
    editor_setup = LoadingBar_setup,

    Prop("Scale9",      "isScale9Enabled",          "setScale9Enabled", "bool",     "false"),
    Prop("CapInsets",   "getCapInsets",             "setCapInsets",     "rect",     "0 0 0 0"),
    Prop("Texture",     "getTexture",               "loadTexture",      "file",     ""),
    Prop("Percent",     "getPercent",               "setPercent",       "int",      "0"),
    EnumProp("Direction",     "getDirection",         "setDirection",     "enum",     "0", {
        {"Left",  LoadingBarTypeLeft , 'LoadingBarTypeLeft'},
        {"Right", LoadingBarTypeRight, 'LoadingBarTypeRight'},
    }),
})

Attribute("CheckBox", "Widget", {
    editor_setup = CheckBox_setup,

    Prop("Check State",         "getSelectedState",                 "setSelectedState",                 "bool",     "false"),

    Prop("BackGround",          "getTextureBackGround",             "loadTextureBackGround",            "file",     ""),
    Prop("BackGroundSelected",  "getTextureBackGroundSelected",     "loadTextureBackGroundSelected",    "file",     ""),
    Prop("FrontCross",          "getTextureFrontCross",             "loadTextureFrontCross",            "file",     ""),
    Prop("BackGroundDisabled",  "getTextureBackGroundDisabled",     "loadTextureBackGroundDisabled",    "file",     ""),
    Prop("FrontCrossDisabled",  "getTextureFrontCrossDisabled",     "loadTextureFrontCrossDisabled",    "file",     ""),
})

Attribute("Slider", "Widget", {
    editor_setup = Slider_setup,

    Prop("Scale9",      "isScale9Enabled",          "setScale9Enabled", "bool",     "false"),
    Prop("Percent",     "getPercent",               "setPercent",       "int",      "0"),
    Prop("BarTexture",       "getTexture",                          "loadBarTexture",                   "file",     ""),
    Prop("ProgressBar",      "getProgressBarTexture",               "loadProgressBarTexture",           "file",     ""),
    Prop("BallNormal",       "getSlidBallNormalTexture",            "loadSlidBallTextureNormal",        "file",     ""),
    Prop("BallPressed",      "getSlidBallPressedTexture",           "loadSlidBallTexturePressed",       "file",     ""),
    Prop("BallDisabled",     "getSlidBallDisabledTexture",          "loadSlidBallTextureDisabled",      "file",     ""),
})

Attribute("TextField", "Widget", {
    editor_setup = TextField_setup,

    Prop("PlaceHolder",       "getPlaceHolder",                     "setPlaceHolder",                "string",       ""),
    Prop("Font Size",         "getFontSize",                        "setFontSize",                   "int",          "0"),
    Prop("Text",              "getStringValue",                     "setText",                       "string",       ""),
    Prop("Password",          "isPasswordEnabled",                  "setPasswordEnabled",            "bool",         "false"),
    Prop("PasswordTxt",       "getPasswordStyleText",               "setPasswordStyleText",          "string",      ""),
    Prop("MaxLengthEnabled",  "isMaxLengthEnabled",                 "setMaxLengthEnabled",           "bool",        "false"),
    Prop("MaxLength",         "getMaxLength",                       "setMaxLength",                  "int",         "0"),

    EnumProp('HAnign',  'getTextHorizontalAlignment',   'setTextHorizontalAlignment', 'enum', '0', {
        {"Left",   kCCTextAlignmentLeft          , 'kCCTextAlignmentLeft'},
        {"Center", kCCTextAlignmentCenter        , 'kCCTextAlignmentCenter'},
        {"Right",  kCCTextAlignmentRight         , 'kCCTextAlignmentRight'},
    }),

    EnumProp('VAnign',  'getTextVerticalAlignment',   'setTextVerticalAlignment', 'enum', '0', {
        {"Top",    kCCVerticalTextAlignmentTop   , 'kCCVerticalTextAlignmentTop'},
        {"Center", kCCVerticalTextAlignmentCenter, 'kCCVerticalTextAlignmentCenter'},
        {"Bottom", kCCVerticalTextAlignmentBottom, 'kCCVerticalTextAlignmentBottom'},
    }),
})

Attribute("Layout", "Widget", {
    editor_setup = Layout_setup,

    Prop("IgnoreSize",  "isIgnoreContentAdaptWithSize",                 "ignoreContentAdaptWithSize", "bool", "false"),
    Prop("Anchor Pos",  "getAnchorPoint",           "setAnchorPoint",   "pos",      "0 0"),

    Prop("BackGroundImage",          "getBackGroundImage",                          "setBackGroundImage",                      "file",     ""),
    Prop("BGScale9",                "isBackGroundImageScale9Enabled",          "setBackGroundImageScale9Enabled", "bool",     "false"),
    Prop("BGCapInsets",             "getBackGroundImageCapInsets",             "setBackGroundImageCapInsets",     "rect",     "0 0 0 0"),

    EnumProp("Layout",         "getLayoutType",     "setLayoutType",     "enum",     "0",    {
        {"ABSOLUTE",          LAYOUT_ABSOLUTE,          'LAYOUT_ABSOLUTE'},
        {"LINEAR_VERTICAL",   LAYOUT_LINEAR_VERTICAL,   'LAYOUT_LINEAR_VERTICAL'}, 
        {"LINEAR_HORIZONTAL", LAYOUT_LINEAR_HORIZONTAL, 'LAYOUT_LINEAR_HORIZONTAL'},
        {"RELATIVE",          LAYOUT_RELATIVE,          'LAYOUT_RELATIVE'},
    }),

    Prop("ClippingEnabled",             "isClippingEnabled",             "setClippingEnabled",     "bool",     "false"),
    EnumProp("ClippingType",         "getClippingType",     "setClippingType",     "enum",     "0",    {
        {"STENCIL",         LAYOUT_CLIPPING_STENCIL, 'LAYOUT_CLIPPING_STENCIL'},
        {"SCISSOR",         LAYOUT_CLIPPING_SCISSOR, 'LAYOUT_CLIPPING_SCISSOR'}, 
    }),
})

Attribute("ScrollView", "Layout", {
    editor_setup = ScrollView_setup,

    EnumProp("Dir",         "getDirection",     "setDirection",     "enum",     "0",    {
        {"VERTICAL",    SCROLLVIEW_DIR_VERTICAL,   'SCROLLVIEW_DIR_VERTICAL'},
        {"HORIZONTAL",  SCROLLVIEW_DIR_HORIZONTAL, 'SCROLLVIEW_DIR_HORIZONTAL'}, 
        {"BOTH",        SCROLLVIEW_DIR_BOTH,       'SCROLLVIEW_DIR_BOTH'}
    }),
    Prop("Bounce",          "isBounceEnabled",                  "setBounceEnabled",           "bool",         "false"),
})

Attribute("ListView", "ScrollView", {
    editor_setup = ListView_setup,
})

Attribute("PageView", "Layout", {
    editor_setup = PageView_setup,
})


local _Attribute = function(name, base, t)
    
    t._custom = true

    return Attribute(name, base, t)
end

return function()
    return defines, _Attribute, Prop, EnumProp
end
